{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Contents\n",
    "- ## 调用函数\n",
    "- ## 定义函数\n",
    "- ## 函数的参数\n",
    "- ## 递归函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- # 函数的参数\n",
    "### 位置参数\n",
    "### 默认参数\n",
    "    必须使用不可变对象如str、None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['END']\n",
      "['END', 'END']\n",
      "-------------------\n",
      "['END'] ['END']\n"
     ]
    }
   ],
   "source": [
    "# 想实现一个在传入列表后添加'END'的函数 默认为空list\n",
    "def add_end(L=[]):\n",
    "    L.append('END')\n",
    "    return L\n",
    "print(add_end())\n",
    "print(add_end()) \n",
    "# 默认参数其实是一个变量 这里是一个list，所以第一次调用之后L变为['END']\n",
    "print('-------------------')\n",
    "\n",
    "def addend(L=None):\n",
    "    # 不可变对象作为默认参数\n",
    "    if L == None:\n",
    "        L = []\n",
    "    L.append('END')\n",
    "    return L\n",
    "print(addend(),addend())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 可变参数（*args）\n",
    "可变参数允许你传入0个或任意个参数，这些可变参数在函数调用时自动组装为一个tuple\n",
    "#### 如果已经有一个list或tuple，可以用*拆分进行传参"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 3, 9.2, 10)\n",
      "194.64\n",
      "(2.3, 89, 0.13, -3)\n",
      "7935.3069\n"
     ]
    }
   ],
   "source": [
    "def calc(*numbers):\n",
    "    # 可以计算不固定个数的数字的平方和\n",
    "    print(numbers) # 可见numbers是一个元组\n",
    "    sum = 0\n",
    "    for n in numbers:\n",
    "        sum = sum + n * n\n",
    "    return sum\n",
    "print(calc(1, 3, 9.2, 10))\n",
    "\n",
    "### *list传参\n",
    "l = [2.3, 89, 0.13, -3]\n",
    "print(calc(*l)) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 关键字参数（**args）\n",
    "关键字参数允许你传入0个或任意个含参数名的参数，这些关键字参数在函数内部自动组装为一个dict\n",
    "#### 已有的dict可以用**进行拆分传参"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Jack \n",
      "age: 24 \n",
      "other: {} \n",
      "\n",
      "name: Mike \n",
      "age: 17 \n",
      "other: {'city': 'L.A.', 'job': 'Drug dealer'} \n",
      "\n",
      "name: Rashibe Rohan \n",
      "age: 29 \n",
      "other: {'nation': 'Japan', 'stand': \"Heaven's door\"} \n",
      "\n"
     ]
    }
   ],
   "source": [
    "def person(name, age, **kw):\n",
    "    print('name:', name, '\\nage:', age, '\\nother:', kw, '\\n')\n",
    "person('Jack', 24)\n",
    "person('Mike', 17, city=\"L.A.\", job='Drug dealer')\n",
    "\n",
    "### **dict传参\n",
    "extra = {\"nation\": \"Japan\", \"stand\": \"Heaven's door\"}\n",
    "person(\"Rashibe Rohan\", 29, **extra)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 命名关键字参数（放在参数列表中的*之后）\n",
    "用来限制可以传入的关键字函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Zack \n",
      "age: 19 \n",
      "city: Tokyo? \n",
      "job: killer \n",
      "\n",
      "name: zx001 \n",
      "age: 30 \n",
      "city: Shanghai \n",
      "job: Coder \n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 比如只希望接受city和job作为关键字函数\n",
    "### 传递这类参数时必须加上参数名，与位置参数不同 而且必须传\n",
    "def pers(name, age, *, city, job):\n",
    "    print('name:', name, '\\nage:', age, '\\ncity:', city, '\\njob:', job,'\\n')\n",
    "\n",
    "pers('Zack', 19, city='Tokyo?', job='killer')\n",
    "# pers('Zack', 19, city='Tokyo?') ERROR:missing 1 required keyword-only argument: 'job'\n",
    "### 可以把命名关键字写成默认参数\n",
    "def pers(name, age, *, city='Shanghai', job='Coder'):\n",
    "    print('name:', name, '\\nage:', age, '\\ncity:', city, '\\njob:', job, '\\n')\n",
    "pers('zx001', '30')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 参数组合\n",
    "#### 定义参数的顺序为：必选参数、默认参数、可变参数、命名关键字参数和关键字参数\n",
    "不过最好别写的太复杂"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 3 args = (4,) kw = {'fifth': '5'}\n",
      "a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'k': 'K', 'j': 'J'}\n",
      "a = 1 b = 2 c = 0 d = 3 kw = {'another': 'kw'}\n",
      "a = 1 b = 2 c = 3 args = () kw = {'d': 88, 'another': 'kwkw'}\n",
      "a = 1 b = 2 c = 3 d = 88 kw = {'another': 'kwkw'}\n"
     ]
    }
   ],
   "source": [
    "def f1(a, b, c=0, *args, **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)\n",
    "\n",
    "def f2(a, b, c=0, *, d, **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)\n",
    "\n",
    "f1(1, 2, 3, 4, fifth='5')\n",
    "f1(1, 2, 3, 'a', 'b', k='K', j='J')\n",
    "f2(1, 2, d=3, another='kw')\n",
    "## 还可以用一个list或tuple与dict作为整体对函数传参\n",
    "args = [1, 2, 3]\n",
    "kws = {\n",
    "    'd': 88, \"another\": 'kwkw'\n",
    "}\n",
    "f1(*args, **kws)\n",
    "f2(*args, **kws)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- # 递归函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "788657867364790503552363213932185062295135977687173263294742533244359449963403342920304284011984623904177212138919638830257642790242637105061926624952829931113462857270763317237396988943922445621451664240254033291864131227428294853277524242407573903240321257405579568660226031904170324062351700858796178922222789623703897374720000000000000000000000000000000000000000000000000\n",
      "788657867364790503552363213932185062295135977687173263294742533244359449963403342920304284011984623904177212138919638830257642790242637105061926624952829931113462857270763317237396988943922445621451664240254033291864131227428294853277524242407573903240321257405579568660226031904170324062351700858796178922222789623703897374720000000000000000000000000000000000000000000000000\n"
     ]
    }
   ],
   "source": [
    "# n!\n",
    "def fact(n):\n",
    "    if n == 1:\n",
    "        return 1\n",
    "    return n * fact(n - 1)\n",
    "# print(fact(100))\n",
    "# 递归次数过多会导致栈溢出 （每进入一个函数调用都会增加一层栈帧）\n",
    "### 防止方法：尾递归优化（函数返回的时候调用本身但是return语句不含表达式）\n",
    "# 所以只会占用一个栈帧\n",
    "\n",
    "def fact1(n):\n",
    "    return fact_iter(n, 1)\n",
    "\n",
    "def fact_iter(num, product):\n",
    "    if num == 1:\n",
    "        return product\n",
    "    return fact_iter(num - 1, num * product)\n",
    "\n",
    "def fact2(n, result=1):\n",
    "    if n == 1:\n",
    "        return result\n",
    "    return fact2(n - 1, n * result)\n",
    "\n",
    "print(fact2(200))\n",
    "\n",
    "## 实际上阶乘可以直接调用math.factorial()方法\n",
    "import math\n",
    "print(math.factorial(200))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
